import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;

// Press Shift twice to open the Search Everywhere dialog and type `show whitespaces`,
// then press Enter. You can now see whitespace characters in your code.
public class Main {

    private static final ThreadFactory THREAD_FACTORY = Thread::new;
    private static final ThreadPoolExecutor THREAD_POOL_EXECUTOR = new ScheduledThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), THREAD_FACTORY);
    private static final ArrayList<Process> PROCESSES = new ArrayList<>();
    private static int currentCount;
    private static int totalCount;
    private static File cacheFileDir;

    public static void main(String[] args) {
        String os = System.getenv("OS");
        printMsg("当前操作系统：" + os);

        if (!os.toLowerCase().startsWith("windows")) {
            printMsg("请使用Windows系统运行本程序！");
            return;
        }
        printMsg("当前环境最大允许线程数： " + Runtime.getRuntime().availableProcessors());
        Scanner scanner = new Scanner(System.in);
        Runtime runtime = Runtime.getRuntime();
        exit(inputDescriptionFile(scanner, runtime));
    }


    /**
     * 输入描述文件的完整路径
     *
     * @param scanner 输入内容扫描器
     * @param runtime 运行环境
     * @return 运行结果码
     */
    private static int inputDescriptionFile(Scanner scanner, Runtime runtime) {
        printMsg("请输入描述文件的完整路径路径(输入exit或quit退出程序)：");
        File descriptionFile;
//        ArrayList<String> videoUrls;
        while (true) {
            boolean nextLine = scanner.hasNextLine();
            if (!nextLine) {
                continue;
            }
            String s = scanner.nextLine().trim();
            if (s.isEmpty()) {
                printMsg("请输入描述文件的完整路径路径(输入exit或quit退出程序)：");
                continue;
            }

            if (s.equalsIgnoreCase("exit") || s.equalsIgnoreCase("quit")) {
                return ConstantsCode.EXIT;
            }
            if (s.startsWith("\"")) {
                s = s.substring(1);
            }
            if (s.endsWith("\"")) {
                s = s.substring(0, s.length() - 1);
            }
            printMsg("输入的路径：" + s);
            descriptionFile = new File(s);
            if (!descriptionFile.exists()) {
                printMsg("输入的文件路径不存在");
                printMsg("请输入描述文件的完整路径路径(输入exit或quit退出程序)：");
                continue;
            }
            if (!descriptionFile.isFile()) {
                printMsg("输入的文件路径不是一个文件");
                printMsg("请输入描述文件的完整路径路径(输入exit或quit退出程序)：");
                continue;
            }
            break;
        }
        String parent = descriptionFile.getParent();
        cacheFileDir = new File(parent, descriptionFile.getName().split("\\.")[0]);
        if (cacheFileDir.exists()) {
            boolean delete = cacheFileDir.delete();
            printMsg("删除已存在的文件夹 " + (delete ? "成功" : "失败"));
        }
        boolean mkdirs = cacheFileDir.mkdirs();
        printMsg("创建文件夹 " + cacheFileDir.getName() + (mkdirs ? "成功" : "失败"));
        //将文件中的信息读取出来，key 为下载地址，value为解密用的key(不需要解密是，value为mull或“”)
        HashMap<String, String> fileInfo = readFileInfo(descriptionFile);
        if (fileInfo == null || fileInfo.isEmpty()) {
            return ConstantsCode.ERROR;
        }
        currentCount = 0;
        totalCount = fileInfo.size();
        printMsg("开始下载文件");
        downLoadFiles(fileInfo);
        while (true) {
            synchronized (Main.class) {
                if (currentCount >= totalCount) {
                    printMsg("等待下载结束：" + currentCount + " / " + totalCount);
                    break;
                }
            }
        }
        String cmd = "cmd /c copy /b " + cacheFileDir.getAbsolutePath() + "\\*.ts " + new File(cacheFileDir, "merged.ts").getAbsolutePath();
        printMsg("执行指令：" + cmd);
        try {
            Process process = runtime.exec(cmd);
            PROCESSES.add(process);
            InputStream inputStream = process.getInputStream();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            while (process.isAlive()) {
                String s = bufferedReader.readLine();
                if (s != null) {
                    printMsg(s);
                }
            }
            printMsg("合并完成！");
        } catch (IOException e) {
            e.printStackTrace();
            return ConstantsCode.ERROR;
        }
        return ConstantsCode.SUCCESS;
    }

    private static void downLoadFiles(HashMap<String, String> fileInfo) {
        Iterator<Map.Entry<String, String>> iterator = fileInfo.entrySet().iterator();
        int currentIndex = 0;
        while (iterator.hasNext()) {
            Map.Entry<String, String> entry = iterator.next();
            int finalCurrentIndex = currentIndex;
            THREAD_POOL_EXECUTOR.execute(() -> {
                try {
                    downloadNetAndDescrypt(entry.getKey(), cacheFileDir.getAbsolutePath(), generateFileName(finalCurrentIndex), entry.getValue());
                } catch (Exception e) {
                    e.printStackTrace();
                    printMsg(e.getMessage());
                }
                currentCount++;
                printMsg("下载进度 " + currentCount + " / " + totalCount);
            });
            currentIndex++;
        }
    }

    private static HashMap<String, String> readFileInfo(File descriptionFile) {
        BufferedReader bufferedReader;
        try {
            bufferedReader = new BufferedReader(new FileReader(descriptionFile));
        } catch (FileNotFoundException e) {
            return null;
        }
        HashMap<String, String> stringBooleanHashMap = new HashMap<>();
        String decryptKey = null;
        try {
            while (true) {
                String cache = bufferedReader.readLine();
                if (cache == null) {
                    break;
                }
                if (cache.isEmpty()) {
                    continue;
                }
                if (cache.startsWith(ConstantsCode.ENCRYPT_KEY)) {
                    printMsg("获取加密信息：" + cache);
                    try {
                        cache = cache.replace(ConstantsCode.ENCRYPT_KEY, "");
                    } catch (Exception e) {
                        continue;
                    }
                    String[] split = cache.split(",");
                    if (split.length >= 2) {

                        String methodInfo;
                        String keyUrlInfo;
                        try {
                            methodInfo = split[0];
                            keyUrlInfo = split[1];
                        } catch (Exception e) {
                            continue;
                        }
                        String method = methodInfo.split("=")[1];
                        if (method.equalsIgnoreCase(ConstantsCode.ENCRYPT_AES_128)) {
                            cache = keyUrlInfo.split("=")[1];
                            String decryptFileUrl = cache.replace("\"", "");
                            if (decryptFileUrl.isEmpty()) {
                                continue;
                            }
                            try {
                                downloadNet(decryptFileUrl, cacheFileDir.getAbsolutePath(), ConstantsCode.ENCRYPT_FILE_NAME);
                            } catch (Exception e) {
                                continue;
                            }
                            File encryptFile = new File(cacheFileDir, ConstantsCode.ENCRYPT_FILE_NAME);
                            decryptKey = readAesKeyFromFile(encryptFile);
                            printMsg("解析到加密信息为 " + method);
                            continue;
                        } else {
                            decryptKey = null;
                            printMsg("解析到未知加密信息 " + method);
                            continue;
                        }
                    } else {
                        String method = cache.split("=")[1];
                        if (method.equalsIgnoreCase(ConstantsCode.ENCRYPT_NONE)) {
                            File encryptFile = new File(cacheFileDir, ConstantsCode.ENCRYPT_FILE_NAME);
                            if (encryptFile.exists()) {
                                encryptFile.delete();
                            }
                            decryptKey = null;
                            printMsg("解析到加密信息关闭 " + ConstantsCode.ENCRYPT_NONE);
                        }
                    }
                }
                if (cache.startsWith("#")) {
                    continue;
                }
                if (!cache.contains(ConstantsCode.HTTP_START) && !cache.contains(ConstantsCode.HTTPS_START)) {
                    continue;
                }
                stringBooleanHashMap.put(cache, decryptKey);
            }
            return stringBooleanHashMap;
        } catch (IOException e) {
            return null;
        } finally {
            try {
                bufferedReader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private static String readAesKeyFromFile(File encryptFile) {
        BufferedReader bufferedReader;
        try {
            bufferedReader = new BufferedReader(new FileReader(encryptFile));
        } catch (FileNotFoundException e) {
            return null;
        }
        try {
            StringBuilder resultBuilder = new StringBuilder();
            while (true) {
                String cache = bufferedReader.readLine();
                if (cache == null) {
                    break;
                }
                resultBuilder.append(cache);
            }
            return resultBuilder.toString();
        } catch (IOException e) {
            return null;
        } finally {
            try {
                bufferedReader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private static void printMsg(String msg) {
        System.out.println(msg);
    }

    /**
     * 退出程序
     */
    private static void exit(int code) {
        for (Process process : PROCESSES) {
            if (process.isAlive()) {
                process.destroy();
            }
        }
        THREAD_POOL_EXECUTOR.shutdownNow();
        printMsg("程序已经退出,结果码：" + code);
    }

    private static void downloadNet(String urlString, String fileDir, String fileName) throws Exception {
        //定义一个URL对象，就是你想下载的图片的URL地址
        URL url = new URL(urlString);
        //打开连接
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        //设置请求方式为"GET"
        conn.setRequestMethod("GET");
        //超时响应时间为10秒
        conn.setConnectTimeout(10 * 1000);
        //通过输入流获取图片数据
        InputStream is = conn.getInputStream();
        //得到图片的二进制数据，以二进制封装得到数据，具有通用性
        byte[] data = readInputStream(is);
        //创建一个文件对象用来保存图片，默认保存当前工程根目录，起名叫Copy.jpg
        File imageFile = new File(fileDir, fileName);
        //创建输出流
        FileOutputStream outStream = new FileOutputStream(imageFile);
        //写入数据
        outStream.write(data);
        //关闭输出流，释放资源
        outStream.close();
    }

    private static void downloadNetAndDescrypt(String urlString, String fileDir, String fileName, String key) throws Exception {
        //定义一个URL对象，就是你想下载的图片的URL地址
        URL url = new URL(urlString);
        //打开连接
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        //设置请求方式为"GET"
        conn.setRequestMethod("GET");
        //超时响应时间为10秒
        conn.setConnectTimeout(10 * 1000);
        //通过输入流获取图片数据
        InputStream is = conn.getInputStream();
        //得到图片的二进制数据，以二进制封装得到数据，具有通用性
        byte[] data = readInputStream(is);
        if (key != null && !key.isEmpty()) {
            data = AesUtil.aesDecry(data, key);
        }
        //创建一个文件对象用来保存图片，默认保存当前工程根目录，起名叫Copy.jpg
        File imageFile = new File(fileDir, fileName);
        //创建输出流
        FileOutputStream outStream = new FileOutputStream(imageFile);
        //写入数据
        outStream.write(data);
        //关闭输出流，释放资源
        outStream.close();
    }

    private static byte[] readInputStream(InputStream inStream) throws Exception {
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        //创建一个Buffer字符串
        byte[] buffer = new byte[1024];
        //每次读取的字符串长度，如果为-1，代表全部读取完毕
        int len;
        //使用一个输入流从buffer里把数据读取出来
        while ((len = inStream.read(buffer)) != -1) {
            //用输出流往buffer里写入数据，中间参数代表从哪个位置开始读，len代表读取的长度
            outStream.write(buffer, 0, len);
        }
        //关闭输入流
        inStream.close();
        //把outStream里的数据写入内存
        return outStream.toByteArray();
    }

    private static String generateFileName(int number) {
        return String.format("%06d.ts", number);
    }
}